if(!require("plotly")) {install.packages("plotly")}

# install.packages("latex2exp")
# install.packages("BiocManager") 
# install.packages("corrplot")
# BiocManager::install("EBImage")

if(!require("lme4")){install.packages("lme4")}
if(!require("lmerTest")){install.packages("lmerTest")}
if(!require("nlme")){install.packages("nlme")}
if(!require("formattable")){install.packages("formattable")}
if(!require("xgboost")){install.packages("xgboost")}
if(!require("processx")) {install.packages("processx")}

library(plotly)
library(lme4)
library(lmerTest)
library(nlme)
library(formattable)
library(xgboost)

### Load libraries
library(EBImage)
library(ggplot2)
library(stringr)
library(gridExtra)
library(latex2exp)
packageVersion('plotly')
[1] ‘4.9.1’
Sys.setenv("plotly_username"="thuynh32")
Sys.setenv("plotly_api_key"="xcSv1yzujDc1IGEwQlr2")
colorBlue = "#007fff"
colorRed = "#ff7f7f"
colorGray = "#cccccc"
colorGreen = "#11ff00"

all_Drive4 <- read.csv('../../../data/TT1/preprocessed/All/TT1_Drive_4_30m_30m.csv')
all_Drive4$Subject <- as.factor(all_Drive4$Subject)
all_Drive4$logPerspiration <- log(all_Drive4$Perspiration)

persons         = c("01",  "02", "03", "04", "05",  "06",  "07", 
                    "09",  "12", "13", "15", "16",  "17",  "18", 
                    "22",  "24", "29", "30", "31",  "32",  "41")

# starting_points = c( 669 , 668 , 676 , 687 , 680 ,  676 ,  678 ,
#                      693 , 722 , 723 , 677 , 679 ,  711 ,  707 ,  
#                      699 , 679 , 684 , 688 , 686 ,  696 ,  702 )
# 
# ending_points   = c( 741 , 786 , 749 , 782 , 736 ,  756 ,  768 ,  
#                      812 , 853 , 792 , 783 , 772 ,  799 ,  781 ,  
#                      777 , 763 , 795 , 791 , 832 ,  755 ,  758 )

peak_points     = c( 67 ,  86 ,  73 ,  73 ,  73 ,  64 ,  73 ,  79 ,  69 ,  64 ,  68 ,  67 ,  77 ,  68 ,  82 ,  67 ,  72 ,  72 ,  71 ,  68 ,  64 )

# Driving time
driving_times = vector(mode="list", length = length(persons))
names(driving_times) <- persons

activity_names = vector(mode="list", length = length(persons))
names(activity_names) <- persons

acc_start_times = vector(mode="list", length = length(persons))
names(acc_start_times) <- persons
acc_end_times = vector(mode="list", length = length(persons))
names(acc_end_times) <- persons

stressor_start_times = vector(mode="list", length = length(persons))
names(stressor_start_times) <- persons
stressor_end_times = vector(mode="list", length = length(persons))
names(stressor_end_times) <- persons

complete_times = vector(mode="list", length = length(persons))
names(complete_times) <- persons

data_baseline = vector(mode="list", length=length(persons))
pp_baseline = vector(mode="list", length=length(persons))

names(data_baseline) <- persons
names(pp_baseline) <- persons

# Number of peaks

PREV_DISTANCE = 600
TRACKING_DISTANCE = 150
DRIVE_MODE = 4
getActivityName <- function(x, fullname=F) {
  if(x == 1) return(ifelse(fullname, "Normal", "NO"))
  if(x == 2) return(ifelse(fullname, "Cognitive", "C"))
  if(x == 3) return(ifelse(fullname, "Motoric", "M"))
}

for (p in persons) {
  pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
  pAcc <- pData[pData$Failure>0.5,] # Failure = 1
  acc_start_times[[p]] <- min(pAcc$Distance)
  acc_end_times[[p]] <- max(pAcc$Distance)
  
  activity_names[[p]] <- getActivityName(pData[pData$Time==60,]$Activity, fullname = T)
  
  pStressor = pData[pData$Activity>1.5,] # Stressor = 2, 3
  if (nrow(pStressor) > 0) {
    stressor_start_times[[p]] <- min(pStressor$Distance)
    stressor_end_times[[p]] <- max(pStressor$Distance)
  } else {
    stressor_start_times[[p]] <- NULL
    stressor_end_times[[p]] <- NULL
  }
}
idx <- 1
plt_AllAcc <- vector(mode="list", length=length(persons)) 
names(plt_AllAcc) <- persons

COLOR_ACC = "#02A3C8"
COLOR_PP = "#F28E8E"
COLOR_BRAKE = "#888888"

y1 <- list(
  tickfont = list(color = COLOR_ACC),
  title="Degree",
  range=c(0, 100)
)
y2 <- list(
  tickfont = list(color = COLOR_PP),
  overlaying = "y",
  side = "right",
  title = "Log Perspiration",
  showgrid = FALSE,
  range=c(min(all_Drive4$ppLogNormalized), max(all_Drive4$ppLogNormalized))
)
  
for (p in persons) {
  pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
  
  # Baseline
  data_baseline[[p]] <- read.csv(str_interp("../../../data/TT1/preprocessed/T0${person}/T0${person}_Drive_1.csv", list(person=p)))
  # Compute the mean
  p_pp_nr <- data_baseline[[p]]$Perspiration
  p_pp_nr <- p_pp_nr[!is.na(p_pp_nr)]
  pp_baseline[[p]] <- log(mean(p_pp_nr))
  
  # Incident
  driving_times[[p]] <- max(pData$Distance)
  
  incident_starting_time <- acc_start_times[[p]] # starting_points[idx]
  incident_ending_time <- acc_end_times[[p]]
  complete_times[[p]] <- ifelse(incident_starting_time + TRACKING_DISTANCE > driving_times[[p]], driving_times[[p]], incident_starting_time + TRACKING_DISTANCE)
  
  from_time <- ifelse(incident_starting_time - PREV_DISTANCE >= 0, incident_starting_time - PREV_DISTANCE, 0)
  to_time <- complete_times[[p]]
  
  # print(paste("From", from_time))
  # print(paste("Incident", incident_starting_time))
  # print(paste("To", to_time))
  
    
  pDataBefore <- pData[pData$Distance < incident_starting_time & pData$Distance >= from_time,]
  pDataAfter <- pData[pData$Distance >= incident_starting_time & pData$Distance <= to_time,]
  
  # print(nrow(pDataBefore))
  # print(nrow(pDataAfter))
  
  ppMeanBefore <- mean(pDataBefore$ppLogNormalized)
  ppMeanAfter <- mean(pDataAfter$ppLogNormalized)
  
  dir.create(file.path('../figures/drive/', paste0('Drive_', DRIVE_MODE)), showWarnings = FALSE)
  fname <- str_interp('../figures/drive/Drive_${drive}/P${person}.svg', list(drive=DRIVE_MODE, person=p)) 
  
  pData <- pData[pData$Distance >= from_time,]
  plot_Acc <- plot_ly(pData, x = ~Distance, height=400, width=900) %>%
              add_trace(name="Acceleration", y = ~Acceleration, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_ACC)) %>% 
              add_trace(name="Brake", y = ~Braking, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_BRAKE)) %>%
              add_trace(name="PP", y = ~ppLogNormalized, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_PP), yaxis = "y2") %>% 
              add_segments(x = min(pData$Distance), xend = max(pData$Distance), y = ppMeanBefore, yend = ppMeanBefore, 
                           yaxis = "y2", name="Mean PP (Before Incident)",
                           line=list(color=COLOR_PP, dash = 'dot')) %>%
              add_segments(x = min(pData$Distance), xend = max(pData$Distance), y = ppMeanAfter, yend = ppMeanAfter, 
                           yaxis = "y2", name="Mean PP (After Incident)",
                           line=list(color="darkred", dash = 'dot')) %>%
              # add_segments(x = min(pData$Distance) - 0.1, xend = max(pData$Distance), y = pp_baseline[[p]], yend = pp_baseline[[p]], 
              #              yaxis = "y2", name="Baseline PP (from Drive 1)",
              #              line=list(color="blue", dash = 'dot')) %>%
    
              layout(
                title=paste0("Subject #", p, " (Stressor=", activity_names[[p]], ")"), 
                xaxis=list(title="Distance [m]", range=c(0)), 
                yaxis=y1, 
                yaxis2=y2, 
                margin = list(l = 50, r = 50, b = 50, t = 50, pad = 4),
                shapes = list(
                  # Holistic period
                  list(type = "rect", fillcolor = "red", 
                       line = list(color = "red"), opacity = 0.3,
                      x0 = incident_starting_time, x1 = incident_ending_time, xref = "x",
                      y0 = 0, y1 = 100, yref = "y"),
                  # Stressor period
                  list(type = "rect", fillcolor = "yellow", 
                       line = list(color = "yellow"), opacity = 0.1,
                      x0 = stressor_start_times[[p]], x1 = incident_starting_time, xref = "x",
                      y0 = 0, y1 = 100, yref = "y"),
                  list(type = "rect", fillcolor = "yellow", 
                       line = list(color = "yellow"), opacity = 0.1,
                      x0 = incident_ending_time, x1 = stressor_end_times[[p]], xref = "x",
                      y0 = 0, y1 = 100, yref = "y")
                ),
                legend = list(x = 0.1, y = 1, bgcolor = "rgba(0,0,0,0)", title="Metric"),
                autosize = F
              )
  
  # orca(plot_PP, fname)
  idx <- idx + 1
  plt_AllAcc[[p]] <- plot_Acc
}

htmltools::tagList(plt_AllAcc)
for (p in persons) {
  # Save image
  orca(plt_AllAcc[[p]], file = paste0("../figures/drive/Drive_4/T0", p, ".png"), scale = 2)
}

-

\

|

/

-

\

|

/

-

\

|

/

-

\

|

/

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

\

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

\

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

\

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 

-

\

|

/

-

\

|

/

-

\

|

/

-

 
idx <- 1
behavioralColumns <- c("Subject", 
                       "Brake_u", 
                       "Brake_std", 
                       "PP_before",
                       "PP_u",  
                       "PP_std",
                       "PP_dev")
behavioralMatrix <- matrix(nrow=length(persons), ncol = length(behavioralColumns))

# Careful about Subject 09
# selected_persons <- persons[persons != "09"]

for (p in persons) {
  pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
  
  incident_starting_time <- acc_start_times[[p]] # starting_points[idx]
  incident_ending_time <- acc_end_times[[p]]
  
  from_time <- ifelse(incident_starting_time - PREV_DISTANCE >= 0, incident_starting_time - PREV_DISTANCE, 0)
  to_time <- complete_times[[p]]
    
  dfBefore <- pData[pData$Distance < incident_starting_time & pData$Distance >= from_time,]
  dfAfter <- pData[pData$Distance >= incident_starting_time & pData$Distance <= to_time,]
  
  # diffSpeed <- mean(dfAfter$Speed) - mean(dfBefore$Speed)
  brakeMean <- mean(dfAfter$Braking)
  brakeStd <- mean(dfAfter$Braking)
  
  ppMean <- mean(dfAfter$ppLogNormalized)
  ppBefore <- mean(dfBefore$ppLogNormalized)
  ppStd <- sd(dfAfter$ppLogNormalized)
  
  mid_avg <- (pp_baseline[[p]] + mean(dfBefore$ppLogNormalized)) / 2
    
  diffPP <- mean(dfAfter$ppLogNormalized) - mean(dfBefore$ppLogNormalized)
  
  behavioralMatrix[idx, ] <- c(p, 
                               round(brakeMean, digits=5), 
                               round(brakeStd, digits=5),
                               round(ppBefore, digits=5),
                               round(ppMean, digits = 5),
                               round(ppStd, digits=5),
                               round(diffPP, digits=5))
  idx <- idx + 1
}

# behavioralMatrix

behavioralDf <- as.data.frame(behavioralMatrix)
names(behavioralDf) <- behavioralColumns

behavioralDf
NA
clusteringDf <- behavioralDf
clusteringDf$Subject <- NULL
# clusteringDf$PP_dev_norm <- as.numeric(clusteringDf$PP_dev) / as.numeric(clusteringDf$PP_u)
# clusteringDf$PP_std_norm <- as.numeric(clusteringDf$PP_std) / as.numeric(clusteringDf$PP_u)
clusteringDf$Brake_u <- NULL
clusteringDf$Brake_std <- NULL
clusteringDf$PP_before <- NULL
clusteringDf$PP_u <- NULL
clusteringDf$PP_std <- NULL
# clusteringDf$PP_dev <- NULL

rownames(clusteringDf) <- paste0("#", persons)

for (col in names(clusteringDf)) {
  clusteringDf[,col] <- as.numeric(as.character(clusteringDf[, col]))
  # clusteringDf[,col] <- scale(clusteringDf[,col])
}
clusteringDf
dfActivity <- all_Drive4[all_Drive4$Time==60,] %>% select(c("Subject", "Activity"))
dfActivity$ActivityName <- sapply(dfActivity$Activity, getActivityName)
dfActivity$Subject <- as.factor(dfActivity$Subject)
rownames(dfActivity) <- NULL
dfActivity %>% select(c("Subject", "ActivityName"))
library(dendextend)

NUMBER_OF_CLUSTERS = 3

color_darkpink = "#e75480"
CLUSTER_BRANCH_COLORS <- c("blue", "darkred", color_darkpink)[1:NUMBER_OF_CLUSTERS]
CLUSTER_LABEL_COLORS <- c("blue", "darkred", color_darkpink)[1:NUMBER_OF_CLUSTERS]

behavioralMatrixClustering <- as.matrix(clusteringDf)
rownames(behavioralMatrixClustering) <- paste0(dfActivity$ActivityName, " - #", persons)
distMatrix <- dist(behavioralMatrixClustering)
hresults <- distMatrix %>% hclust

hc <- hresults %>% 
      as.dendrogram %>%
      set("nodes_cex", NUMBER_OF_CLUSTERS) %>%
      set("labels_col", value = CLUSTER_LABEL_COLORS, k=NUMBER_OF_CLUSTERS) %>%
      # set("leaves_pch", 19) %>%
      # set("leaves_col", value = c("gray"), k=NUMBER_OF_CLUSTERS) %>%    
      set("branches_k_color", value=CLUSTER_BRANCH_COLORS, k=NUMBER_OF_CLUSTERS)

plot(hc)
legend("topright", 
     title="Drive=Failure \nHierachical Clustering",
     legend = c("Exceptional Increase of PP" , "Slightly Increase of PP" , "No-change or Decrease of PP"), 
     col = c("darkred", "pink" , "blue"),
     pch = c(20,20,20), bty = "n",  pt.cex = 1.5, cex = 0.8 , 
     text.col = "black", horiz = FALSE, inset = c(0.4, 0.1))

# Store clustering data
fPath <- str_interp("../../../data/TT1/preprocessed/Analysis/TT1_Drive_4_PP.csv")
dfx <- clusteringDf
dfx <- cbind(persons, dfx, dfActivity$ActivityName)
names(dfx) <- c("Subject", "PP_Dev", "Activity")
write.csv(dfx, fPath, row.names = F)
library(cluster)
fit <- kmeans(clusteringDf, 3)
clusplot(clusteringDf, fit$cluster, color=TRUE, shade=TRUE,
   labels=3, lines=0)

silhouette_score <- function(k){
  km <- kmeans(clusteringDf, centers = k, nstart=25)
  ss <- silhouette(km$cluster, dist(clusteringDf))
  mean(ss[, 3])
}
k <- 2:10
avg_sil <- sapply(k, silhouette_score)
plot(k, type='b', avg_sil, xlab='Number of clusters', ylab='Average Silhouette Scores', frame=FALSE)

# plot(clusteringDf$PP_Dev)
# ggplot(clusteringDf, aes(PP_Mean)) + 
#   geom_density(alpha = 0.3)
# plot(clusteringDf$PP_Dev)
# ggplot(clusteringDf, aes(PP_Dev)) + 
#   geom_density(alpha = 0.3)

ML Model

df_Drive3 <- read.csv('../data/output/Drive_3/corr_Prev_15s_Next_5s.csv')
df_Drive3$Subject <- as.factor(df_Drive3$Subject)

CLUSTER_THRESHOLD = 4
clusters <- cutree(hresults, h=CLUSTER_THRESHOLD)
getClusterName <- function(s) {
  sID <- str_replace(s, "Subject ", "")
  if (str_sub(sID[1], 1,1) != "#") {
    sID <- paste0("#", sID)
  }
  return(ifelse(clusters[sID] == 1, 1, 0))
}

colClass <- sapply(persons, getClusterName)
df_Drive3$clsPP <- as.factor(colClass)
# install.packages("randomForest")
# install.packages("MLmetrics")
# install.packages("caret")
library(randomForest)
library(ROCR)
library(caret)

# Train
trainAndTestModel <- function(pData) {
  pSelected <- pData
  pSelected$Subject <- NULL
  # pSelected$`Steering..std..` <- NULL
  
  # View(pSelected)
  
  nChicken = nrow(pSelected[pSelected$clsPP == 1,])
  nNormal = nrow(pSelected[pSelected$clsPP == 0,])
  
  print(paste("Chicken =", nChicken))
  print(paste("Normal =", nNormal))
  
  # Split dataset
  set.seed(43)
  n_folds = 4
  
  folds <- createFolds(factor(pSelected$clsPP), k = n_folds, list = FALSE)
  # folds <- cut(seq(1,nrow(pSelected)), breaks=n_folds,labels=FALSE)
  pSelected$fold <- folds
  
  kf_acc <- double(n_folds)
  kf_prec <- double(n_folds)
  kf_recall <- double(n_folds)
  kf_f1 <- double(n_folds)
  kf_npv <- double(n_folds)
  
  for (i in 1:n_folds) {
    # train_ind <- sample(seq_len(nrow(pSelected)), size = smp_size)
    # train <- pSelected[train_ind, ]
    # test <- pSelected[-train_ind, ]
    
    # test_ind <- which(folds==i, arr.ind=T)
    train <- pSelected[pSelected$fold != i, ] %>% select(-fold)
    test <- pSelected[pSelected$fold == i, ] %>% select(-fold)
    
    print(paste("Train =", nrow(train), "Pos =", nrow(train[train$clsPP == 1,]), "Neg =", nrow(train[train$clsPP == 0,])))
    print(paste("Test =", nrow(test), "Pos =", nrow(test[test$clsPP == 1,]), "Neg =", nrow(test[test$clsPP == 0,])))
    
    # Model Train
    model <- randomForest(clsPP~., data = train, importance = TRUE, ntree=10)
    print(importance(model))
    # print(model)
    
    # Test
    testX <- select(test, -clsPP)
    predY <- predict(model, testX)
    testY <- test$clsPP
    
    #print(levels(predY))
    #print(levels(testY))
    
    # Evaluate
    # print(table(predY, testY))
    
    kf_acc[i] <- mean(predY==testY)
    kf_recall[i] <- sensitivity(predY, testY)
    kf_prec[i] <- posPredValue(predY, testY, positive = 1)
    kf_f1[i] <- (2 * kf_recall[i] * kf_prec[i]) / (kf_recall[i] + kf_prec[i])
    kf_npv[i] <- negPredValue(predY, testY, positive = 1)
    
    # print(paste("Perf:", kf_acc[i], kf_recall[i], kf_prec[i], kf_f1[i], kf_npv[i]))
  }
  
  # XGB
  param <- list(objective       = "binary:logistic", 
               booster          = "gbtree",
               eval_metric      = "auc",
               eta              = 0.1,
               max_depth        = 5,
               gamma            =0.8,
               min_child_weight = 3,
               subsample        = 1,
               colsample_bytree = 0.5,
               stratified       = F
  )
  pSelected <- pSelected %>% mutate(clsPP=ifelse(clsPP==1, 1, 0))
  
  # AUC
  # aucs = c()
  # xgb_m = xgb.cv(   params               = param,
  #                   data = as.matrix(pSelected %>% select(-clsPP)) ,
  #                   label =  pSelected$clsPP,
  #                   nrounds             = 500,
  #                   verbose             = F,
  #                   prediction          = T,
  #                   maximize            = T,
  #                   nfold = n_folds,
  #                   metrics  = "auc",
  #                   early_stopping_rounds = 100,
  #                   scale_pos_weight = 1)
  # aucs =  c(aucs,as.numeric(xgb_m$evaluation_log[xgb_m$best_iteration,"test_auc_mean"]))
  
  # Get average performance
  acc <- mean(kf_acc)
  prec <- mean(kf_prec)
  recall <- mean(kf_recall)
  f1 <- mean(kf_f1)
  npv <- mean(kf_npv)
  # auc <- mean(aucs)
 
  # Return 
  rtn <- list(
    accuracy=acc,
    recall=recall,
    precision=prec,
    f1=f1,
    npv=npv
    # auc=auc
  )
  
  print(rtn)
}

trainAndTestModel(df_Drive3)
library(caret)
library(VGAM)

NUM_FEATURES = 8

pSelected <- df_Drive3
pSelected$Subject <- NULL

fit <- vglm(clsPP~., family=multinomial, data=pSelected)
# summarize the fit
summary(fit)

probabilities <- predict(fit, pSelected[,1:NUM_FEATURES], type="response")
predictions <- apply(probabilities, 1, which.max)
predictions[which(predictions=="High")] <- levels(pSelected$clsPP)[1]
predictions[which(predictions=="Low")] <- levels(pSelected$clsPP)[2]
# summarize accuracy
table(predictions, pSelected$clsPP)

# x <- pSelected[,1:NUM_FEATURES]
# y <- pSelected[,NUM_FEATURES + 1]
# # fit model
# fit <- plsda(x, y, probMethod="Bayes")
# # summarize the fit
# summary(fit)
# # make predictions
# predictions <- predict(fit, pSelected[,1:NUM_FEATURES])
# # summarize accuracy
# table(predictions, pSelected$clsPP)
library(caret)

pSelected <- df_Drive3
pSelected$Subject <- NULL
pSelected$clsPP <- as.numeric(colClass)

# define training control
train_control <- trainControl(method = "cv", number = 4)

# train the model on training set
model <- train(clsPP ~ .,
               data = pSelected,
               trControl = train_control,
               method = "glm",
               family=binomial())

# print cv scores
summary(model)

predTrain = predict(model, newdata=pSelected, type="raw")
table(pSelected$clsPP, predTrain > 0.5)
formula = clsPP ~ .
modellm <- glm(formula, data=pSelected)
plot(modellm)
summary(model)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmlmKCFyZXF1aXJlKCJwbG90bHkiKSkge2luc3RhbGwucGFja2FnZXMoInBsb3RseSIpfQoKIyBpbnN0YWxsLnBhY2thZ2VzKCJsYXRleDJleHAiKQojIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikgCiMgaW5zdGFsbC5wYWNrYWdlcygiY29ycnBsb3QiKQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJFQkltYWdlIikKCmlmKCFyZXF1aXJlKCJsbWU0Iikpe2luc3RhbGwucGFja2FnZXMoImxtZTQiKX0KaWYoIXJlcXVpcmUoImxtZXJUZXN0Iikpe2luc3RhbGwucGFja2FnZXMoImxtZXJUZXN0Iil9CmlmKCFyZXF1aXJlKCJubG1lIikpe2luc3RhbGwucGFja2FnZXMoIm5sbWUiKX0KaWYoIXJlcXVpcmUoImZvcm1hdHRhYmxlIikpe2luc3RhbGwucGFja2FnZXMoImZvcm1hdHRhYmxlIil9CmlmKCFyZXF1aXJlKCJ4Z2Jvb3N0Iikpe2luc3RhbGwucGFja2FnZXMoInhnYm9vc3QiKX0KaWYoIXJlcXVpcmUoInByb2Nlc3N4IikpIHtpbnN0YWxsLnBhY2thZ2VzKCJwcm9jZXNzeCIpfQoKbGlicmFyeShwbG90bHkpCmxpYnJhcnkobG1lNCkKbGlicmFyeShsbWVyVGVzdCkKbGlicmFyeShubG1lKQpsaWJyYXJ5KGZvcm1hdHRhYmxlKQpsaWJyYXJ5KHhnYm9vc3QpCgojIyMgTG9hZCBsaWJyYXJpZXMKbGlicmFyeShFQkltYWdlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobGF0ZXgyZXhwKQpwYWNrYWdlVmVyc2lvbigncGxvdGx5JykKU3lzLnNldGVudigicGxvdGx5X3VzZXJuYW1lIj0idGh1eW5oMzIiKQpTeXMuc2V0ZW52KCJwbG90bHlfYXBpX2tleSI9InhjU3YxeXp1akRjMUlHRXdRbHIyIikKCmBgYAoKYGBge3J9CmNvbG9yQmx1ZSA9ICIjMDA3ZmZmIgpjb2xvclJlZCA9ICIjZmY3ZjdmIgpjb2xvckdyYXkgPSAiI2NjY2NjYyIKY29sb3JHcmVlbiA9ICIjMTFmZjAwIgoKYWxsX0RyaXZlNCA8LSByZWFkLmNzdignLi4vLi4vLi4vZGF0YS9UVDEvcHJlcHJvY2Vzc2VkL0FsbC9UVDFfRHJpdmVfNF8zMG1fMzBtLmNzdicpCmFsbF9Ecml2ZTQkU3ViamVjdCA8LSBhcy5mYWN0b3IoYWxsX0RyaXZlNCRTdWJqZWN0KQphbGxfRHJpdmU0JGxvZ1BlcnNwaXJhdGlvbiA8LSBsb2coYWxsX0RyaXZlNCRQZXJzcGlyYXRpb24pCgpwZXJzb25zICAgICAgICAgPSBjKCIwMSIsICAiMDIiLCAiMDMiLCAiMDQiLCAiMDUiLCAgIjA2IiwgICIwNyIsIAogICAgICAgICAgICAgICAgICAgICIwOSIsICAiMTIiLCAiMTMiLCAiMTUiLCAiMTYiLCAgIjE3IiwgICIxOCIsIAogICAgICAgICAgICAgICAgICAgICIyMiIsICAiMjQiLCAiMjkiLCAiMzAiLCAiMzEiLCAgIjMyIiwgICI0MSIpCgojIHN0YXJ0aW5nX3BvaW50cyA9IGMoIDY2OSAsIDY2OCAsIDY3NiAsIDY4NyAsIDY4MCAsICA2NzYgLCAgNjc4ICwKIyAgICAgICAgICAgICAgICAgICAgICA2OTMgLCA3MjIgLCA3MjMgLCA2NzcgLCA2NzkgLCAgNzExICwgIDcwNyAsICAKIyAgICAgICAgICAgICAgICAgICAgICA2OTkgLCA2NzkgLCA2ODQgLCA2ODggLCA2ODYgLCAgNjk2ICwgIDcwMiApCiMgCiMgZW5kaW5nX3BvaW50cyAgID0gYyggNzQxICwgNzg2ICwgNzQ5ICwgNzgyICwgNzM2ICwgIDc1NiAsICA3NjggLCAgCiMgICAgICAgICAgICAgICAgICAgICAgODEyICwgODUzICwgNzkyICwgNzgzICwgNzcyICwgIDc5OSAsICA3ODEgLCAgCiMgICAgICAgICAgICAgICAgICAgICAgNzc3ICwgNzYzICwgNzk1ICwgNzkxICwgODMyICwgIDc1NSAsICA3NTggKQoKcGVha19wb2ludHMgICAgID0gYyggNjcgLCAgODYgLCAgNzMgLCAgNzMgLCAgNzMgLCAgNjQgLCAgNzMgLCAgNzkgLCAgNjkgLCAgNjQgLCAgNjggLCAgNjcgLCAgNzcgLCAgNjggLCAgODIgLCAgNjcgLCAgNzIgLCAgNzIgLCAgNzEgLCAgNjggLCAgNjQgKQoKIyBEcml2aW5nIHRpbWUKZHJpdmluZ190aW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhkcml2aW5nX3RpbWVzKSA8LSBwZXJzb25zCgphY3Rpdml0eV9uYW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhhY3Rpdml0eV9uYW1lcykgPC0gcGVyc29ucwoKYWNjX3N0YXJ0X3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKGFjY19zdGFydF90aW1lcykgPC0gcGVyc29ucwphY2NfZW5kX3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKGFjY19lbmRfdGltZXMpIDwtIHBlcnNvbnMKCnN0cmVzc29yX3N0YXJ0X3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKHN0cmVzc29yX3N0YXJ0X3RpbWVzKSA8LSBwZXJzb25zCnN0cmVzc29yX2VuZF90aW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhzdHJlc3Nvcl9lbmRfdGltZXMpIDwtIHBlcnNvbnMKCmNvbXBsZXRlX3RpbWVzID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGVyc29ucykpCm5hbWVzKGNvbXBsZXRlX3RpbWVzKSA8LSBwZXJzb25zCgpkYXRhX2Jhc2VsaW5lID0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHBlcnNvbnMpKQpwcF9iYXNlbGluZSA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChwZXJzb25zKSkKCm5hbWVzKGRhdGFfYmFzZWxpbmUpIDwtIHBlcnNvbnMKbmFtZXMocHBfYmFzZWxpbmUpIDwtIHBlcnNvbnMKCiMgTnVtYmVyIG9mIHBlYWtzCgpQUkVWX0RJU1RBTkNFID0gNjAwClRSQUNLSU5HX0RJU1RBTkNFID0gMTUwCkRSSVZFX01PREUgPSA0CmBgYAoKYGBge3J9CmdldEFjdGl2aXR5TmFtZSA8LSBmdW5jdGlvbih4LCBmdWxsbmFtZT1GKSB7CiAgaWYoeCA9PSAxKSByZXR1cm4oaWZlbHNlKGZ1bGxuYW1lLCAiTm9ybWFsIiwgIk5PIikpCiAgaWYoeCA9PSAyKSByZXR1cm4oaWZlbHNlKGZ1bGxuYW1lLCAiQ29nbml0aXZlIiwgIkMiKSkKICBpZih4ID09IDMpIHJldHVybihpZmVsc2UoZnVsbG5hbWUsICJNb3RvcmljIiwgIk0iKSkKfQoKZm9yIChwIGluIHBlcnNvbnMpIHsKICBwRGF0YSA8LSBhbGxfRHJpdmU0W2FsbF9Ecml2ZTQkU3ViamVjdD09YXMuaW50ZWdlcihwKSB8IGFsbF9Ecml2ZTQkU3ViamVjdD09cCxdCiAgcEFjYyA8LSBwRGF0YVtwRGF0YSRGYWlsdXJlPjAuNSxdICMgRmFpbHVyZSA9IDEKICBhY2Nfc3RhcnRfdGltZXNbW3BdXSA8LSBtaW4ocEFjYyREaXN0YW5jZSkKICBhY2NfZW5kX3RpbWVzW1twXV0gPC0gbWF4KHBBY2MkRGlzdGFuY2UpCiAgCiAgYWN0aXZpdHlfbmFtZXNbW3BdXSA8LSBnZXRBY3Rpdml0eU5hbWUocERhdGFbcERhdGEkVGltZT09NjAsXSRBY3Rpdml0eSwgZnVsbG5hbWUgPSBUKQogIAogIHBTdHJlc3NvciA9IHBEYXRhW3BEYXRhJEFjdGl2aXR5PjEuNSxdICMgU3RyZXNzb3IgPSAyLCAzCiAgaWYgKG5yb3cocFN0cmVzc29yKSA+IDApIHsKICAgIHN0cmVzc29yX3N0YXJ0X3RpbWVzW1twXV0gPC0gbWluKHBTdHJlc3NvciREaXN0YW5jZSkKICAgIHN0cmVzc29yX2VuZF90aW1lc1tbcF1dIDwtIG1heChwU3RyZXNzb3IkRGlzdGFuY2UpCiAgfSBlbHNlIHsKICAgIHN0cmVzc29yX3N0YXJ0X3RpbWVzW1twXV0gPC0gTlVMTAogICAgc3RyZXNzb3JfZW5kX3RpbWVzW1twXV0gPC0gTlVMTAogIH0KfQpgYGAKCmBgYHtyfQppZHggPC0gMQpwbHRfQWxsQWNjIDwtIHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChwZXJzb25zKSkgCm5hbWVzKHBsdF9BbGxBY2MpIDwtIHBlcnNvbnMKCkNPTE9SX0FDQyA9ICIjMDJBM0M4IgpDT0xPUl9QUCA9ICIjRjI4RThFIgpDT0xPUl9CUkFLRSA9ICIjODg4ODg4IgoKeTEgPC0gbGlzdCgKICB0aWNrZm9udCA9IGxpc3QoY29sb3IgPSBDT0xPUl9BQ0MpLAogIHRpdGxlPSJEZWdyZWUiLAogIHJhbmdlPWMoMCwgMTAwKQopCnkyIDwtIGxpc3QoCiAgdGlja2ZvbnQgPSBsaXN0KGNvbG9yID0gQ09MT1JfUFApLAogIG92ZXJsYXlpbmcgPSAieSIsCiAgc2lkZSA9ICJyaWdodCIsCiAgdGl0bGUgPSAiTG9nIFBlcnNwaXJhdGlvbiIsCiAgc2hvd2dyaWQgPSBGQUxTRSwKICByYW5nZT1jKG1pbihhbGxfRHJpdmU0JHBwTG9nTm9ybWFsaXplZCksIG1heChhbGxfRHJpdmU0JHBwTG9nTm9ybWFsaXplZCkpCikKICAKZm9yIChwIGluIHBlcnNvbnMpIHsKICBwRGF0YSA8LSBhbGxfRHJpdmU0W2FsbF9Ecml2ZTQkU3ViamVjdD09YXMuaW50ZWdlcihwKSB8IGFsbF9Ecml2ZTQkU3ViamVjdD09cCxdCiAgCiAgIyBCYXNlbGluZQogIGRhdGFfYmFzZWxpbmVbW3BdXSA8LSByZWFkLmNzdihzdHJfaW50ZXJwKCIuLi8uLi8uLi9kYXRhL1RUMS9wcmVwcm9jZXNzZWQvVDAke3BlcnNvbn0vVDAke3BlcnNvbn1fRHJpdmVfMS5jc3YiLCBsaXN0KHBlcnNvbj1wKSkpCiAgIyBDb21wdXRlIHRoZSBtZWFuCiAgcF9wcF9uciA8LSBkYXRhX2Jhc2VsaW5lW1twXV0kUGVyc3BpcmF0aW9uCiAgcF9wcF9uciA8LSBwX3BwX25yWyFpcy5uYShwX3BwX25yKV0KICBwcF9iYXNlbGluZVtbcF1dIDwtIGxvZyhtZWFuKHBfcHBfbnIpKQogIAogICMgSW5jaWRlbnQKICBkcml2aW5nX3RpbWVzW1twXV0gPC0gbWF4KHBEYXRhJERpc3RhbmNlKQogIAogIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgPC0gYWNjX3N0YXJ0X3RpbWVzW1twXV0gIyBzdGFydGluZ19wb2ludHNbaWR4XQogIGluY2lkZW50X2VuZGluZ190aW1lIDwtIGFjY19lbmRfdGltZXNbW3BdXQogIGNvbXBsZXRlX3RpbWVzW1twXV0gPC0gaWZlbHNlKGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgKyBUUkFDS0lOR19ESVNUQU5DRSA+IGRyaXZpbmdfdGltZXNbW3BdXSwgZHJpdmluZ190aW1lc1tbcF1dLCBpbmNpZGVudF9zdGFydGluZ190aW1lICsgVFJBQ0tJTkdfRElTVEFOQ0UpCiAgCiAgZnJvbV90aW1lIDwtIGlmZWxzZShpbmNpZGVudF9zdGFydGluZ190aW1lIC0gUFJFVl9ESVNUQU5DRSA+PSAwLCBpbmNpZGVudF9zdGFydGluZ190aW1lIC0gUFJFVl9ESVNUQU5DRSwgMCkKICB0b190aW1lIDwtIGNvbXBsZXRlX3RpbWVzW1twXV0KICAKICAjIHByaW50KHBhc3RlKCJGcm9tIiwgZnJvbV90aW1lKSkKICAjIHByaW50KHBhc3RlKCJJbmNpZGVudCIsIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUpKQogICMgcHJpbnQocGFzdGUoIlRvIiwgdG9fdGltZSkpCiAgCiAgICAKICBwRGF0YUJlZm9yZSA8LSBwRGF0YVtwRGF0YSREaXN0YW5jZSA8IGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgJiBwRGF0YSREaXN0YW5jZSA+PSBmcm9tX3RpbWUsXQogIHBEYXRhQWZ0ZXIgPC0gcERhdGFbcERhdGEkRGlzdGFuY2UgPj0gaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAmIHBEYXRhJERpc3RhbmNlIDw9IHRvX3RpbWUsXQogIAogICMgcHJpbnQobnJvdyhwRGF0YUJlZm9yZSkpCiAgIyBwcmludChucm93KHBEYXRhQWZ0ZXIpKQogIAogIHBwTWVhbkJlZm9yZSA8LSBtZWFuKHBEYXRhQmVmb3JlJHBwTG9nTm9ybWFsaXplZCkKICBwcE1lYW5BZnRlciA8LSBtZWFuKHBEYXRhQWZ0ZXIkcHBMb2dOb3JtYWxpemVkKQogIAogIGRpci5jcmVhdGUoZmlsZS5wYXRoKCcuLi9maWd1cmVzL2RyaXZlLycsIHBhc3RlMCgnRHJpdmVfJywgRFJJVkVfTU9ERSkpLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKICBmbmFtZSA8LSBzdHJfaW50ZXJwKCcuLi9maWd1cmVzL2RyaXZlL0RyaXZlXyR7ZHJpdmV9L1Ake3BlcnNvbn0uc3ZnJywgbGlzdChkcml2ZT1EUklWRV9NT0RFLCBwZXJzb249cCkpIAogIAogIHBEYXRhIDwtIHBEYXRhW3BEYXRhJERpc3RhbmNlID49IGZyb21fdGltZSxdCiAgcGxvdF9BY2MgPC0gcGxvdF9seShwRGF0YSwgeCA9IH5EaXN0YW5jZSwgaGVpZ2h0PTQwMCwgd2lkdGg9OTAwKSAlPiUKICAgICAgICAgICAgICBhZGRfdHJhY2UobmFtZT0iQWNjZWxlcmF0aW9uIiwgeSA9IH5BY2NlbGVyYXRpb24sIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLCBsaW5lPWxpc3Qod2lkdGg9MS41LCBjb2xvcj1DT0xPUl9BQ0MpKSAlPiUgCiAgICAgICAgICAgICAgYWRkX3RyYWNlKG5hbWU9IkJyYWtlIiwgeSA9IH5CcmFraW5nLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywgbGluZT1saXN0KHdpZHRoPTEuNSwgY29sb3I9Q09MT1JfQlJBS0UpKSAlPiUKICAgICAgICAgICAgICBhZGRfdHJhY2UobmFtZT0iUFAiLCB5ID0gfnBwTG9nTm9ybWFsaXplZCwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsIGxpbmU9bGlzdCh3aWR0aD0xLjUsIGNvbG9yPUNPTE9SX1BQKSwgeWF4aXMgPSAieTIiKSAlPiUgCiAgICAgICAgICAgICAgYWRkX3NlZ21lbnRzKHggPSBtaW4ocERhdGEkRGlzdGFuY2UpLCB4ZW5kID0gbWF4KHBEYXRhJERpc3RhbmNlKSwgeSA9IHBwTWVhbkJlZm9yZSwgeWVuZCA9IHBwTWVhbkJlZm9yZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gInkyIiwgbmFtZT0iTWVhbiBQUCAoQmVmb3JlIEluY2lkZW50KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmU9bGlzdChjb2xvcj1DT0xPUl9QUCwgZGFzaCA9ICdkb3QnKSkgJT4lCiAgICAgICAgICAgICAgYWRkX3NlZ21lbnRzKHggPSBtaW4ocERhdGEkRGlzdGFuY2UpLCB4ZW5kID0gbWF4KHBEYXRhJERpc3RhbmNlKSwgeSA9IHBwTWVhbkFmdGVyLCB5ZW5kID0gcHBNZWFuQWZ0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9ICJ5MiIsIG5hbWU9Ik1lYW4gUFAgKEFmdGVyIEluY2lkZW50KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmU9bGlzdChjb2xvcj0iZGFya3JlZCIsIGRhc2ggPSAnZG90JykpICU+JQogICAgICAgICAgICAgICMgYWRkX3NlZ21lbnRzKHggPSBtaW4ocERhdGEkRGlzdGFuY2UpIC0gMC4xLCB4ZW5kID0gbWF4KHBEYXRhJERpc3RhbmNlKSwgeSA9IHBwX2Jhc2VsaW5lW1twXV0sIHllbmQgPSBwcF9iYXNlbGluZVtbcF1dLCAKICAgICAgICAgICAgICAjICAgICAgICAgICAgICB5YXhpcyA9ICJ5MiIsIG5hbWU9IkJhc2VsaW5lIFBQIChmcm9tIERyaXZlIDEpIiwKICAgICAgICAgICAgICAjICAgICAgICAgICAgICBsaW5lPWxpc3QoY29sb3I9ImJsdWUiLCBkYXNoID0gJ2RvdCcpKSAlPiUKICAgIAogICAgICAgICAgICAgIGxheW91dCgKICAgICAgICAgICAgICAgIHRpdGxlPXBhc3RlMCgiU3ViamVjdCAjIiwgcCwgIiAoU3RyZXNzb3I9IiwgYWN0aXZpdHlfbmFtZXNbW3BdXSwgIikiKSwgCiAgICAgICAgICAgICAgICB4YXhpcz1saXN0KHRpdGxlPSJEaXN0YW5jZSBbbV0iLCByYW5nZT1jKDApKSwgCiAgICAgICAgICAgICAgICB5YXhpcz15MSwgCiAgICAgICAgICAgICAgICB5YXhpczI9eTIsIAogICAgICAgICAgICAgICAgbWFyZ2luID0gbGlzdChsID0gNTAsIHIgPSA1MCwgYiA9IDUwLCB0ID0gNTAsIHBhZCA9IDQpLAogICAgICAgICAgICAgICAgc2hhcGVzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgIyBIb2xpc3RpYyBwZXJpb2QKICAgICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gInJlY3QiLCBmaWxsY29sb3IgPSAicmVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAicmVkIiksIG9wYWNpdHkgPSAwLjMsCiAgICAgICAgICAgICAgICAgICAgICB4MCA9IGluY2lkZW50X3N0YXJ0aW5nX3RpbWUsIHgxID0gaW5jaWRlbnRfZW5kaW5nX3RpbWUsIHhyZWYgPSAieCIsCiAgICAgICAgICAgICAgICAgICAgICB5MCA9IDAsIHkxID0gMTAwLCB5cmVmID0gInkiKSwKICAgICAgICAgICAgICAgICAgIyBTdHJlc3NvciBwZXJpb2QKICAgICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gInJlY3QiLCBmaWxsY29sb3IgPSAieWVsbG93IiwgCiAgICAgICAgICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAieWVsbG93IiksIG9wYWNpdHkgPSAwLjEsCiAgICAgICAgICAgICAgICAgICAgICB4MCA9IHN0cmVzc29yX3N0YXJ0X3RpbWVzW1twXV0sIHgxID0gaW5jaWRlbnRfc3RhcnRpbmdfdGltZSwgeHJlZiA9ICJ4IiwKICAgICAgICAgICAgICAgICAgICAgIHkwID0gMCwgeTEgPSAxMDAsIHlyZWYgPSAieSIpLAogICAgICAgICAgICAgICAgICBsaXN0KHR5cGUgPSAicmVjdCIsIGZpbGxjb2xvciA9ICJ5ZWxsb3ciLCAKICAgICAgICAgICAgICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJ5ZWxsb3ciKSwgb3BhY2l0eSA9IDAuMSwKICAgICAgICAgICAgICAgICAgICAgIHgwID0gaW5jaWRlbnRfZW5kaW5nX3RpbWUsIHgxID0gc3RyZXNzb3JfZW5kX3RpbWVzW1twXV0sIHhyZWYgPSAieCIsCiAgICAgICAgICAgICAgICAgICAgICB5MCA9IDAsIHkxID0gMTAwLCB5cmVmID0gInkiKQogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIGxlZ2VuZCA9IGxpc3QoeCA9IDAuMSwgeSA9IDEsIGJnY29sb3IgPSAicmdiYSgwLDAsMCwwKSIsIHRpdGxlPSJNZXRyaWMiKSwKICAgICAgICAgICAgICAgIGF1dG9zaXplID0gRgogICAgICAgICAgICAgICkKICAKICAjIG9yY2EocGxvdF9QUCwgZm5hbWUpCiAgaWR4IDwtIGlkeCArIDEKICBwbHRfQWxsQWNjW1twXV0gPC0gcGxvdF9BY2MKfQoKaHRtbHRvb2xzOjp0YWdMaXN0KHBsdF9BbGxBY2MpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CmZvciAocCBpbiBwZXJzb25zKSB7CiAgIyBTYXZlIGltYWdlCiAgb3JjYShwbHRfQWxsQWNjW1twXV0sIGZpbGUgPSBwYXN0ZTAoIi4uL2ZpZ3VyZXMvZHJpdmUvRHJpdmVfNC9UMCIsIHAsICIucG5nIiksIHNjYWxlID0gMikKfQpgYGAKCmBgYHtyfQppZHggPC0gMQpiZWhhdmlvcmFsQ29sdW1ucyA8LSBjKCJTdWJqZWN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIkJyYWtlX3UiLCAKICAgICAgICAgICAgICAgICAgICAgICAiQnJha2Vfc3RkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIlBQX2JlZm9yZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIlBQX3UiLCAgCiAgICAgICAgICAgICAgICAgICAgICAgIlBQX3N0ZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIlBQX2RldiIpCmJlaGF2aW9yYWxNYXRyaXggPC0gbWF0cml4KG5yb3c9bGVuZ3RoKHBlcnNvbnMpLCBuY29sID0gbGVuZ3RoKGJlaGF2aW9yYWxDb2x1bW5zKSkKCiMgQ2FyZWZ1bCBhYm91dCBTdWJqZWN0IDA5CiMgc2VsZWN0ZWRfcGVyc29ucyA8LSBwZXJzb25zW3BlcnNvbnMgIT0gIjA5Il0KCmZvciAocCBpbiBwZXJzb25zKSB7CiAgcERhdGEgPC0gYWxsX0RyaXZlNFthbGxfRHJpdmU0JFN1YmplY3Q9PWFzLmludGVnZXIocCkgfCBhbGxfRHJpdmU0JFN1YmplY3Q9PXAsXQogIAogIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgPC0gYWNjX3N0YXJ0X3RpbWVzW1twXV0gIyBzdGFydGluZ19wb2ludHNbaWR4XQogIGluY2lkZW50X2VuZGluZ190aW1lIDwtIGFjY19lbmRfdGltZXNbW3BdXQogIAogIGZyb21fdGltZSA8LSBpZmVsc2UoaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAtIFBSRVZfRElTVEFOQ0UgPj0gMCwgaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAtIFBSRVZfRElTVEFOQ0UsIDApCiAgdG9fdGltZSA8LSBjb21wbGV0ZV90aW1lc1tbcF1dCiAgICAKICBkZkJlZm9yZSA8LSBwRGF0YVtwRGF0YSREaXN0YW5jZSA8IGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgJiBwRGF0YSREaXN0YW5jZSA+PSBmcm9tX3RpbWUsXQogIGRmQWZ0ZXIgPC0gcERhdGFbcERhdGEkRGlzdGFuY2UgPj0gaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAmIHBEYXRhJERpc3RhbmNlIDw9IHRvX3RpbWUsXQogIAogICMgZGlmZlNwZWVkIDwtIG1lYW4oZGZBZnRlciRTcGVlZCkgLSBtZWFuKGRmQmVmb3JlJFNwZWVkKQogIGJyYWtlTWVhbiA8LSBtZWFuKGRmQWZ0ZXIkQnJha2luZykKICBicmFrZVN0ZCA8LSBtZWFuKGRmQWZ0ZXIkQnJha2luZykKICAKICBwcE1lYW4gPC0gbWVhbihkZkFmdGVyJHBwTG9nTm9ybWFsaXplZCkKICBwcEJlZm9yZSA8LSBtZWFuKGRmQmVmb3JlJHBwTG9nTm9ybWFsaXplZCkKICBwcFN0ZCA8LSBzZChkZkFmdGVyJHBwTG9nTm9ybWFsaXplZCkKICAKICBtaWRfYXZnIDwtIChwcF9iYXNlbGluZVtbcF1dICsgbWVhbihkZkJlZm9yZSRwcExvZ05vcm1hbGl6ZWQpKSAvIDIKICAgIAogIGRpZmZQUCA8LSBtZWFuKGRmQWZ0ZXIkcHBMb2dOb3JtYWxpemVkKSAtIG1lYW4oZGZCZWZvcmUkcHBMb2dOb3JtYWxpemVkKQogIAogIGJlaGF2aW9yYWxNYXRyaXhbaWR4LCBdIDwtIGMocCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChicmFrZU1lYW4sIGRpZ2l0cz01KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChicmFrZVN0ZCwgZGlnaXRzPTUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocHBCZWZvcmUsIGRpZ2l0cz01KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHBwTWVhbiwgZGlnaXRzID0gNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChwcFN0ZCwgZGlnaXRzPTUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoZGlmZlBQLCBkaWdpdHM9NSkpCiAgaWR4IDwtIGlkeCArIDEKfQoKIyBiZWhhdmlvcmFsTWF0cml4CgpiZWhhdmlvcmFsRGYgPC0gYXMuZGF0YS5mcmFtZShiZWhhdmlvcmFsTWF0cml4KQpuYW1lcyhiZWhhdmlvcmFsRGYpIDwtIGJlaGF2aW9yYWxDb2x1bW5zCgpiZWhhdmlvcmFsRGYKCmBgYAoKCmBgYHtyfQpjbHVzdGVyaW5nRGYgPC0gYmVoYXZpb3JhbERmCmNsdXN0ZXJpbmdEZiRTdWJqZWN0IDwtIE5VTEwKIyBjbHVzdGVyaW5nRGYkUFBfZGV2X25vcm0gPC0gYXMubnVtZXJpYyhjbHVzdGVyaW5nRGYkUFBfZGV2KSAvIGFzLm51bWVyaWMoY2x1c3RlcmluZ0RmJFBQX3UpCiMgY2x1c3RlcmluZ0RmJFBQX3N0ZF9ub3JtIDwtIGFzLm51bWVyaWMoY2x1c3RlcmluZ0RmJFBQX3N0ZCkgLyBhcy5udW1lcmljKGNsdXN0ZXJpbmdEZiRQUF91KQpjbHVzdGVyaW5nRGYkQnJha2VfdSA8LSBOVUxMCmNsdXN0ZXJpbmdEZiRCcmFrZV9zdGQgPC0gTlVMTApjbHVzdGVyaW5nRGYkUFBfYmVmb3JlIDwtIE5VTEwKY2x1c3RlcmluZ0RmJFBQX3UgPC0gTlVMTApjbHVzdGVyaW5nRGYkUFBfc3RkIDwtIE5VTEwKIyBjbHVzdGVyaW5nRGYkUFBfZGV2IDwtIE5VTEwKCnJvd25hbWVzKGNsdXN0ZXJpbmdEZikgPC0gcGFzdGUwKCIjIiwgcGVyc29ucykKCmZvciAoY29sIGluIG5hbWVzKGNsdXN0ZXJpbmdEZikpIHsKICBjbHVzdGVyaW5nRGZbLGNvbF0gPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoY2x1c3RlcmluZ0RmWywgY29sXSkpCiAgIyBjbHVzdGVyaW5nRGZbLGNvbF0gPC0gc2NhbGUoY2x1c3RlcmluZ0RmWyxjb2xdKQp9CmNsdXN0ZXJpbmdEZgpgYGAKCmBgYHtyfQpkZkFjdGl2aXR5IDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRUaW1lPT02MCxdICU+JSBzZWxlY3QoYygiU3ViamVjdCIsICJBY3Rpdml0eSIpKQpkZkFjdGl2aXR5JEFjdGl2aXR5TmFtZSA8LSBzYXBwbHkoZGZBY3Rpdml0eSRBY3Rpdml0eSwgZ2V0QWN0aXZpdHlOYW1lKQpkZkFjdGl2aXR5JFN1YmplY3QgPC0gYXMuZmFjdG9yKGRmQWN0aXZpdHkkU3ViamVjdCkKcm93bmFtZXMoZGZBY3Rpdml0eSkgPC0gTlVMTApkZkFjdGl2aXR5ICU+JSBzZWxlY3QoYygiU3ViamVjdCIsICJBY3Rpdml0eU5hbWUiKSkKYGBgCgpgYGB7cn0KbGlicmFyeShkZW5kZXh0ZW5kKQoKTlVNQkVSX09GX0NMVVNURVJTID0gMwoKY29sb3JfZGFya3BpbmsgPSAiI2U3NTQ4MCIKQ0xVU1RFUl9CUkFOQ0hfQ09MT1JTIDwtIGMoImJsdWUiLCAiZGFya3JlZCIsIGNvbG9yX2RhcmtwaW5rKVsxOk5VTUJFUl9PRl9DTFVTVEVSU10KQ0xVU1RFUl9MQUJFTF9DT0xPUlMgPC0gYygiYmx1ZSIsICJkYXJrcmVkIiwgY29sb3JfZGFya3BpbmspWzE6TlVNQkVSX09GX0NMVVNURVJTXQoKYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcgPC0gYXMubWF0cml4KGNsdXN0ZXJpbmdEZikKcm93bmFtZXMoYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcpIDwtIHBhc3RlMChkZkFjdGl2aXR5JEFjdGl2aXR5TmFtZSwgIiAtICMiLCBwZXJzb25zKQpkaXN0TWF0cml4IDwtIGRpc3QoYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcpCmhyZXN1bHRzIDwtIGRpc3RNYXRyaXggJT4lIGhjbHVzdAoKaGMgPC0gaHJlc3VsdHMgJT4lIAogICAgICBhcy5kZW5kcm9ncmFtICU+JQogICAgICBzZXQoIm5vZGVzX2NleCIsIE5VTUJFUl9PRl9DTFVTVEVSUykgJT4lCiAgICAgIHNldCgibGFiZWxzX2NvbCIsIHZhbHVlID0gQ0xVU1RFUl9MQUJFTF9DT0xPUlMsIGs9TlVNQkVSX09GX0NMVVNURVJTKSAlPiUKICAgICAgIyBzZXQoImxlYXZlc19wY2giLCAxOSkgJT4lCiAgICAgICMgc2V0KCJsZWF2ZXNfY29sIiwgdmFsdWUgPSBjKCJncmF5IiksIGs9TlVNQkVSX09GX0NMVVNURVJTKSAlPiUgICAgCiAgICAgIHNldCgiYnJhbmNoZXNfa19jb2xvciIsIHZhbHVlPUNMVVNURVJfQlJBTkNIX0NPTE9SUywgaz1OVU1CRVJfT0ZfQ0xVU1RFUlMpCgpwbG90KGhjKQpsZWdlbmQoInRvcHJpZ2h0IiwgCiAgICAgdGl0bGU9IkRyaXZlPUZhaWx1cmUgXG5IaWVyYWNoaWNhbCBDbHVzdGVyaW5nIiwKICAgICBsZWdlbmQgPSBjKCJFeGNlcHRpb25hbCBJbmNyZWFzZSBvZiBQUCIgLCAiU2xpZ2h0bHkgSW5jcmVhc2Ugb2YgUFAiICwgIk5vLWNoYW5nZSBvciBEZWNyZWFzZSBvZiBQUCIpLCAKICAgICBjb2wgPSBjKCJkYXJrcmVkIiwgInBpbmsiICwgImJsdWUiKSwKICAgICBwY2ggPSBjKDIwLDIwLDIwKSwgYnR5ID0gIm4iLCAgcHQuY2V4ID0gMS41LCBjZXggPSAwLjggLCAKICAgICB0ZXh0LmNvbCA9ICJibGFjayIsIGhvcml6ID0gRkFMU0UsIGluc2V0ID0gYygwLjQsIDAuMSkpCmBgYAoKYGBge3J9CiMgU3RvcmUgY2x1c3RlcmluZyBkYXRhCmZQYXRoIDwtIHN0cl9pbnRlcnAoIi4uLy4uLy4uL2RhdGEvVFQxL3ByZXByb2Nlc3NlZC9BbmFseXNpcy9UVDFfRHJpdmVfNF9QUC5jc3YiKQpkZnggPC0gY2x1c3RlcmluZ0RmCmRmeCA8LSBjYmluZChwZXJzb25zLCBkZngsIGRmQWN0aXZpdHkkQWN0aXZpdHlOYW1lKQpuYW1lcyhkZngpIDwtIGMoIlN1YmplY3QiLCAiUFBfRGV2IiwgIkFjdGl2aXR5IikKd3JpdGUuY3N2KGRmeCwgZlBhdGgsIHJvdy5uYW1lcyA9IEYpCmBgYAoKYGBge3J9CmxpYnJhcnkoY2x1c3RlcikKZml0IDwtIGttZWFucyhjbHVzdGVyaW5nRGYsIDMpCmNsdXNwbG90KGNsdXN0ZXJpbmdEZiwgZml0JGNsdXN0ZXIsIGNvbG9yPVRSVUUsIHNoYWRlPVRSVUUsCiAgIGxhYmVscz0zLCBsaW5lcz0wKQpgYGAKCmBgYHtyfQpzaWxob3VldHRlX3Njb3JlIDwtIGZ1bmN0aW9uKGspewogIGttIDwtIGttZWFucyhjbHVzdGVyaW5nRGYsIGNlbnRlcnMgPSBrLCBuc3RhcnQ9MjUpCiAgc3MgPC0gc2lsaG91ZXR0ZShrbSRjbHVzdGVyLCBkaXN0KGNsdXN0ZXJpbmdEZikpCiAgbWVhbihzc1ssIDNdKQp9CmsgPC0gMjoxMAphdmdfc2lsIDwtIHNhcHBseShrLCBzaWxob3VldHRlX3Njb3JlKQpwbG90KGssIHR5cGU9J2InLCBhdmdfc2lsLCB4bGFiPSdOdW1iZXIgb2YgY2x1c3RlcnMnLCB5bGFiPSdBdmVyYWdlIFNpbGhvdWV0dGUgU2NvcmVzJywgZnJhbWU9RkFMU0UpCmBgYAoKCmBgYHtyfQojIHBsb3QoY2x1c3RlcmluZ0RmJFBQX0RldikKIyBnZ3Bsb3QoY2x1c3RlcmluZ0RmLCBhZXMoUFBfTWVhbikpICsgCiMgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpCmBgYAoKYGBge3J9CiMgcGxvdChjbHVzdGVyaW5nRGYkUFBfRGV2KQojIGdncGxvdChjbHVzdGVyaW5nRGYsIGFlcyhQUF9EZXYpKSArIAojICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4zKQpgYGAKCiMjIyBNTCBNb2RlbApgYGB7cn0KZGZfRHJpdmUzIDwtIHJlYWQuY3N2KCcuLi9kYXRhL291dHB1dC9Ecml2ZV8zL2NvcnJfUHJldl8xNXNfTmV4dF81cy5jc3YnKQpkZl9Ecml2ZTMkU3ViamVjdCA8LSBhcy5mYWN0b3IoZGZfRHJpdmUzJFN1YmplY3QpCgpDTFVTVEVSX1RIUkVTSE9MRCA9IDQKY2x1c3RlcnMgPC0gY3V0cmVlKGhyZXN1bHRzLCBoPUNMVVNURVJfVEhSRVNIT0xEKQpnZXRDbHVzdGVyTmFtZSA8LSBmdW5jdGlvbihzKSB7CiAgc0lEIDwtIHN0cl9yZXBsYWNlKHMsICJTdWJqZWN0ICIsICIiKQogIGlmIChzdHJfc3ViKHNJRFsxXSwgMSwxKSAhPSAiIyIpIHsKICAgIHNJRCA8LSBwYXN0ZTAoIiMiLCBzSUQpCiAgfQogIHJldHVybihpZmVsc2UoY2x1c3RlcnNbc0lEXSA9PSAxLCAxLCAwKSkKfQoKY29sQ2xhc3MgPC0gc2FwcGx5KHBlcnNvbnMsIGdldENsdXN0ZXJOYW1lKQpkZl9Ecml2ZTMkY2xzUFAgPC0gYXMuZmFjdG9yKGNvbENsYXNzKQpgYGAKCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpCiMgaW5zdGFsbC5wYWNrYWdlcygiTUxtZXRyaWNzIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KFJPQ1IpCmxpYnJhcnkoY2FyZXQpCgojIFRyYWluCnRyYWluQW5kVGVzdE1vZGVsIDwtIGZ1bmN0aW9uKHBEYXRhKSB7CiAgcFNlbGVjdGVkIDwtIHBEYXRhCiAgcFNlbGVjdGVkJFN1YmplY3QgPC0gTlVMTAogICMgcFNlbGVjdGVkJGBTdGVlcmluZy4uc3RkLi5gIDwtIE5VTEwKICAKICAjIFZpZXcocFNlbGVjdGVkKQogIAogIG5DaGlja2VuID0gbnJvdyhwU2VsZWN0ZWRbcFNlbGVjdGVkJGNsc1BQID09IDEsXSkKICBuTm9ybWFsID0gbnJvdyhwU2VsZWN0ZWRbcFNlbGVjdGVkJGNsc1BQID09IDAsXSkKICAKICBwcmludChwYXN0ZSgiQ2hpY2tlbiA9IiwgbkNoaWNrZW4pKQogIHByaW50KHBhc3RlKCJOb3JtYWwgPSIsIG5Ob3JtYWwpKQogIAogICMgU3BsaXQgZGF0YXNldAogIHNldC5zZWVkKDQzKQogIG5fZm9sZHMgPSA0CiAgCiAgZm9sZHMgPC0gY3JlYXRlRm9sZHMoZmFjdG9yKHBTZWxlY3RlZCRjbHNQUCksIGsgPSBuX2ZvbGRzLCBsaXN0ID0gRkFMU0UpCiAgIyBmb2xkcyA8LSBjdXQoc2VxKDEsbnJvdyhwU2VsZWN0ZWQpKSwgYnJlYWtzPW5fZm9sZHMsbGFiZWxzPUZBTFNFKQogIHBTZWxlY3RlZCRmb2xkIDwtIGZvbGRzCiAgCiAga2ZfYWNjIDwtIGRvdWJsZShuX2ZvbGRzKQogIGtmX3ByZWMgPC0gZG91YmxlKG5fZm9sZHMpCiAga2ZfcmVjYWxsIDwtIGRvdWJsZShuX2ZvbGRzKQogIGtmX2YxIDwtIGRvdWJsZShuX2ZvbGRzKQogIGtmX25wdiA8LSBkb3VibGUobl9mb2xkcykKICAKICBmb3IgKGkgaW4gMTpuX2ZvbGRzKSB7CiAgICAjIHRyYWluX2luZCA8LSBzYW1wbGUoc2VxX2xlbihucm93KHBTZWxlY3RlZCkpLCBzaXplID0gc21wX3NpemUpCiAgICAjIHRyYWluIDwtIHBTZWxlY3RlZFt0cmFpbl9pbmQsIF0KICAgICMgdGVzdCA8LSBwU2VsZWN0ZWRbLXRyYWluX2luZCwgXQogICAgCiAgICAjIHRlc3RfaW5kIDwtIHdoaWNoKGZvbGRzPT1pLCBhcnIuaW5kPVQpCiAgICB0cmFpbiA8LSBwU2VsZWN0ZWRbcFNlbGVjdGVkJGZvbGQgIT0gaSwgXSAlPiUgc2VsZWN0KC1mb2xkKQogICAgdGVzdCA8LSBwU2VsZWN0ZWRbcFNlbGVjdGVkJGZvbGQgPT0gaSwgXSAlPiUgc2VsZWN0KC1mb2xkKQogICAgCiAgICBwcmludChwYXN0ZSgiVHJhaW4gPSIsIG5yb3codHJhaW4pLCAiUG9zID0iLCBucm93KHRyYWluW3RyYWluJGNsc1BQID09IDEsXSksICJOZWcgPSIsIG5yb3codHJhaW5bdHJhaW4kY2xzUFAgPT0gMCxdKSkpCiAgICBwcmludChwYXN0ZSgiVGVzdCA9IiwgbnJvdyh0ZXN0KSwgIlBvcyA9IiwgbnJvdyh0ZXN0W3Rlc3QkY2xzUFAgPT0gMSxdKSwgIk5lZyA9IiwgbnJvdyh0ZXN0W3Rlc3QkY2xzUFAgPT0gMCxdKSkpCiAgICAKICAgICMgTW9kZWwgVHJhaW4KICAgIG1vZGVsIDwtIHJhbmRvbUZvcmVzdChjbHNQUH4uLCBkYXRhID0gdHJhaW4sIGltcG9ydGFuY2UgPSBUUlVFLCBudHJlZT0xMCkKICAgIHByaW50KGltcG9ydGFuY2UobW9kZWwpKQogICAgIyBwcmludChtb2RlbCkKICAgIAogICAgIyBUZXN0CiAgICB0ZXN0WCA8LSBzZWxlY3QodGVzdCwgLWNsc1BQKQogICAgcHJlZFkgPC0gcHJlZGljdChtb2RlbCwgdGVzdFgpCiAgICB0ZXN0WSA8LSB0ZXN0JGNsc1BQCiAgICAKICAgICNwcmludChsZXZlbHMocHJlZFkpKQogICAgI3ByaW50KGxldmVscyh0ZXN0WSkpCiAgICAKICAgICMgRXZhbHVhdGUKICAgICMgcHJpbnQodGFibGUocHJlZFksIHRlc3RZKSkKICAgIAogICAga2ZfYWNjW2ldIDwtIG1lYW4ocHJlZFk9PXRlc3RZKQogICAga2ZfcmVjYWxsW2ldIDwtIHNlbnNpdGl2aXR5KHByZWRZLCB0ZXN0WSkKICAgIGtmX3ByZWNbaV0gPC0gcG9zUHJlZFZhbHVlKHByZWRZLCB0ZXN0WSwgcG9zaXRpdmUgPSAxKQogICAga2ZfZjFbaV0gPC0gKDIgKiBrZl9yZWNhbGxbaV0gKiBrZl9wcmVjW2ldKSAvIChrZl9yZWNhbGxbaV0gKyBrZl9wcmVjW2ldKQogICAga2ZfbnB2W2ldIDwtIG5lZ1ByZWRWYWx1ZShwcmVkWSwgdGVzdFksIHBvc2l0aXZlID0gMSkKICAgIAogICAgIyBwcmludChwYXN0ZSgiUGVyZjoiLCBrZl9hY2NbaV0sIGtmX3JlY2FsbFtpXSwga2ZfcHJlY1tpXSwga2ZfZjFbaV0sIGtmX25wdltpXSkpCiAgfQogIAogICMgWEdCCiAgcGFyYW0gPC0gbGlzdChvYmplY3RpdmUgICAgICAgPSAiYmluYXJ5OmxvZ2lzdGljIiwgCiAgICAgICAgICAgICAgIGJvb3N0ZXIgICAgICAgICAgPSAiZ2J0cmVlIiwKICAgICAgICAgICAgICAgZXZhbF9tZXRyaWMgICAgICA9ICJhdWMiLAogICAgICAgICAgICAgICBldGEgICAgICAgICAgICAgID0gMC4xLAogICAgICAgICAgICAgICBtYXhfZGVwdGggICAgICAgID0gNSwKICAgICAgICAgICAgICAgZ2FtbWEgICAgICAgICAgICA9MC44LAogICAgICAgICAgICAgICBtaW5fY2hpbGRfd2VpZ2h0ID0gMywKICAgICAgICAgICAgICAgc3Vic2FtcGxlICAgICAgICA9IDEsCiAgICAgICAgICAgICAgIGNvbHNhbXBsZV9ieXRyZWUgPSAwLjUsCiAgICAgICAgICAgICAgIHN0cmF0aWZpZWQgICAgICAgPSBGCiAgKQogIHBTZWxlY3RlZCA8LSBwU2VsZWN0ZWQgJT4lIG11dGF0ZShjbHNQUD1pZmVsc2UoY2xzUFA9PTEsIDEsIDApKQogIAogICMgQVVDCiAgIyBhdWNzID0gYygpCiAgIyB4Z2JfbSA9IHhnYi5jdiggICBwYXJhbXMgICAgICAgICAgICAgICA9IHBhcmFtLAogICMgICAgICAgICAgICAgICAgICAgZGF0YSA9IGFzLm1hdHJpeChwU2VsZWN0ZWQgJT4lIHNlbGVjdCgtY2xzUFApKSAsCiAgIyAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICBwU2VsZWN0ZWQkY2xzUFAsCiAgIyAgICAgICAgICAgICAgICAgICBucm91bmRzICAgICAgICAgICAgID0gNTAwLAogICMgICAgICAgICAgICAgICAgICAgdmVyYm9zZSAgICAgICAgICAgICA9IEYsCiAgIyAgICAgICAgICAgICAgICAgICBwcmVkaWN0aW9uICAgICAgICAgID0gVCwKICAjICAgICAgICAgICAgICAgICAgIG1heGltaXplICAgICAgICAgICAgPSBULAogICMgICAgICAgICAgICAgICAgICAgbmZvbGQgPSBuX2ZvbGRzLAogICMgICAgICAgICAgICAgICAgICAgbWV0cmljcyAgPSAiYXVjIiwKICAjICAgICAgICAgICAgICAgICAgIGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDEwMCwKICAjICAgICAgICAgICAgICAgICAgIHNjYWxlX3Bvc193ZWlnaHQgPSAxKQogICMgYXVjcyA9ICBjKGF1Y3MsYXMubnVtZXJpYyh4Z2JfbSRldmFsdWF0aW9uX2xvZ1t4Z2JfbSRiZXN0X2l0ZXJhdGlvbiwidGVzdF9hdWNfbWVhbiJdKSkKICAKICAjIEdldCBhdmVyYWdlIHBlcmZvcm1hbmNlCiAgYWNjIDwtIG1lYW4oa2ZfYWNjKQogIHByZWMgPC0gbWVhbihrZl9wcmVjKQogIHJlY2FsbCA8LSBtZWFuKGtmX3JlY2FsbCkKICBmMSA8LSBtZWFuKGtmX2YxKQogIG5wdiA8LSBtZWFuKGtmX25wdikKICAjIGF1YyA8LSBtZWFuKGF1Y3MpCiAKICAjIFJldHVybiAKICBydG4gPC0gbGlzdCgKICAgIGFjY3VyYWN5PWFjYywKICAgIHJlY2FsbD1yZWNhbGwsCiAgICBwcmVjaXNpb249cHJlYywKICAgIGYxPWYxLAogICAgbnB2PW5wdgogICAgIyBhdWM9YXVjCiAgKQogIAogIHByaW50KHJ0bikKfQoKdHJhaW5BbmRUZXN0TW9kZWwoZGZfRHJpdmUzKQpgYGAKCgpgYGB7cn0KbGlicmFyeShjYXJldCkKbGlicmFyeShWR0FNKQoKTlVNX0ZFQVRVUkVTID0gOAoKcFNlbGVjdGVkIDwtIGRmX0RyaXZlMwpwU2VsZWN0ZWQkU3ViamVjdCA8LSBOVUxMCgpmaXQgPC0gdmdsbShjbHNQUH4uLCBmYW1pbHk9bXVsdGlub21pYWwsIGRhdGE9cFNlbGVjdGVkKQojIHN1bW1hcml6ZSB0aGUgZml0CnN1bW1hcnkoZml0KQoKcHJvYmFiaWxpdGllcyA8LSBwcmVkaWN0KGZpdCwgcFNlbGVjdGVkWywxOk5VTV9GRUFUVVJFU10sIHR5cGU9InJlc3BvbnNlIikKcHJlZGljdGlvbnMgPC0gYXBwbHkocHJvYmFiaWxpdGllcywgMSwgd2hpY2gubWF4KQpwcmVkaWN0aW9uc1t3aGljaChwcmVkaWN0aW9ucz09IkhpZ2giKV0gPC0gbGV2ZWxzKHBTZWxlY3RlZCRjbHNQUClbMV0KcHJlZGljdGlvbnNbd2hpY2gocHJlZGljdGlvbnM9PSJMb3ciKV0gPC0gbGV2ZWxzKHBTZWxlY3RlZCRjbHNQUClbMl0KIyBzdW1tYXJpemUgYWNjdXJhY3kKdGFibGUocHJlZGljdGlvbnMsIHBTZWxlY3RlZCRjbHNQUCkKCiMgeCA8LSBwU2VsZWN0ZWRbLDE6TlVNX0ZFQVRVUkVTXQojIHkgPC0gcFNlbGVjdGVkWyxOVU1fRkVBVFVSRVMgKyAxXQojICMgZml0IG1vZGVsCiMgZml0IDwtIHBsc2RhKHgsIHksIHByb2JNZXRob2Q9IkJheWVzIikKIyAjIHN1bW1hcml6ZSB0aGUgZml0CiMgc3VtbWFyeShmaXQpCiMgIyBtYWtlIHByZWRpY3Rpb25zCiMgcHJlZGljdGlvbnMgPC0gcHJlZGljdChmaXQsIHBTZWxlY3RlZFssMTpOVU1fRkVBVFVSRVNdKQojICMgc3VtbWFyaXplIGFjY3VyYWN5CiMgdGFibGUocHJlZGljdGlvbnMsIHBTZWxlY3RlZCRjbHNQUCkKCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQoKcFNlbGVjdGVkIDwtIGRmX0RyaXZlMwpwU2VsZWN0ZWQkU3ViamVjdCA8LSBOVUxMCnBTZWxlY3RlZCRjbHNQUCA8LSBhcy5udW1lcmljKGNvbENsYXNzKQoKIyBkZWZpbmUgdHJhaW5pbmcgY29udHJvbAp0cmFpbl9jb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSA0KQoKIyB0cmFpbiB0aGUgbW9kZWwgb24gdHJhaW5pbmcgc2V0Cm1vZGVsIDwtIHRyYWluKGNsc1BQIH4gLiwKICAgICAgICAgICAgICAgZGF0YSA9IHBTZWxlY3RlZCwKICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5fY29udHJvbCwKICAgICAgICAgICAgICAgbWV0aG9kID0gImdsbSIsCiAgICAgICAgICAgICAgIGZhbWlseT1iaW5vbWlhbCgpKQoKIyBwcmludCBjdiBzY29yZXMKc3VtbWFyeShtb2RlbCkKCnByZWRUcmFpbiA9IHByZWRpY3QobW9kZWwsIG5ld2RhdGE9cFNlbGVjdGVkLCB0eXBlPSJyYXciKQp0YWJsZShwU2VsZWN0ZWQkY2xzUFAsIHByZWRUcmFpbiA+IDAuNSkKCmBgYAoKYGBge3J9CmZvcm11bGEgPSBjbHNQUCB+IC4KbW9kZWxsbSA8LSBnbG0oZm9ybXVsYSwgZGF0YT1wU2VsZWN0ZWQpCnBsb3QobW9kZWxsbSkKc3VtbWFyeShtb2RlbCkKYGBgCgoK